package htw.io;

import java.io.*;
import htw.io.exceptions.*;

/**
 * Die Klasse ObjectIO bietet die Moeglichkeit Objekte 
 * des Typs Serializable zu speichern und zu laden.
 *
 * @author		Steffen Schuhmann
 * @version		0.02beta, 01.02.2012
 */
 
 // TODO Klasse muss noch getestet werden!!!!!!!!!
 
public class ObjectIO {
	
	// Klassenattribute
	private static final String BLANK 				= "";
	
	// Konstatnte Zeichenketten fuer Fehlermeldungen
	private static final String CLASS_NOT_FOUND 	= "Klasse nicht gefunden!";
	private static final String STREAM_CORRUPTED	= "Stream ist inkonsitent!";
	private static final String OPTIONAL_DATA		= "Keine Objektdaten gefunden!";
	private static final String LOAD_IO				= "I/O Fehler beim laden!";
	private static final String INVALID_CLASS		= "Fehler bei Serialisierung der Klasse!";
	private static final String NOT_SERIALIZABLE	= "Objekt/e kann/koennen nicht serialisiert werden!";
	private static final String WRITE_IO			= "I/O Fehler beim Schreiben!";
	private static final String FILE_NOT_FOUND		= "Datei nicht gefunden!";
	private static final String IS_NO_FILE			= "Datei ist keine verwendbare Datei!";
	private static final String CAN_NOT_READ		= "Datei kann nicht gelesen werden!";
	private static final String CAN_NOT_WRITE		= "In Datei kann nicht geschrieben werden";
	private static final String STRING_NULL			= "String zeigt auf eine null-Referenz";
	private static final String STRING_IS_EMPTY		= "String ist leer!";
	private static final String OBJECT_NULL			= "Objekt zeigt auf eine null-Referenz!";
	
	/**
	 * Oeffnet eine File Referenz auf eine Datei.
	 * Dabei wird der Dateiname, fileName auf Inhalt und
	 * Null-Referenz geprueft. Zudem wird die Datei
	 * geprueft ob es sich um eine Existierende- und "echte" Datei	 
	 * handelt bei der lese- und schreibzugriff gewaehleistet ist.
	 *
	 * @param 		fileName Der Absolute oder der Relative Dateiname.
	 * @return		Referenz auf das geoeffnete File Objekt.
	 * @exception	FileNotFoundException Die Datei existiert nicht.
	 * @exception	IsNoFileException Das File-Objekt zeigt auf keine gueltige Datei.
	 * @exception	CanNotReadException Es bestehen keine Leserechte auf der Datei.
	 * @exception	CanNotWriteException Es besteht kein Schreibrecht auf der Datei.
	 */	
	protected static File openFile(String fileName) throws Exception {
		checkString(fileName);
		File file = new File(fileName);
		
		if(!file.exists())
			throw new FileNotFoundException(FILE_NOT_FOUND);
		if(!file.isFile()) 
			throw new IsNoFileException(IS_NO_FILE);
		if(!file.canRead())
			throw new CanNotReadException(CAN_NOT_READ);
		if(!file.canWrite())
			throw new CanNotWriteException(CAN_NOT_WRITE); 
		
		return file;
	}
	
	/**
	 * Erstellt eine Datei mit dem Dateinamen der in fileName spezifiziert ist.
	 * Der Dateiname kann dabei Absolut oder Realtiv zum Arbeitsverzeichniss angegeben
	 * werden.
	 *
	 * @param fileName Der Absolute oder Relative Dateiname.
	 * @return true falls eine neue Datei angelegt wurde,
	 *			false wenn nicht.
	 * @exception FileAlreadyExistsException Wird geworfen falls keine Datei ueberschrieben werden soll, diese jedoch existiert.
	 */
	protected static boolean createFile(String fileName, boolean override) throws Exception {
		checkString(fileName);
		
		File file = new File(fileName);
		
		if(override == true) {
			file.createNewFile();
			return true;
		}
		
		if(override == false) {
			if(file.exists()) 
				throw new FileAlreadyExistsException();
			
			return false;
		}
		return false;
	}
	
	/**
	 * Prueft ob ein String leer ist oder auf eine null-Referenz zeigt.
	 *
	 * @param string Der zu pruefende String.
	 * @exception NullPointerException Wird geweorfen wenn der String leer ist.
	 * @exception StringIsEmptyException Wird geworfen wenn der String leer ist.
	 */
	protected static void checkString(String string) throws Exception {
		if(string == null)
			throw new NullPointerException(STRING_NULL);
		if(BLANK.equals(string))
			throw new StringIsEmptyException(STRING_IS_EMPTY);
	}
	
	/**
	 * Speichert ein Objekt vom Typ Serializable in der Datei 
	 * mit dem Name der Variable fileName, falls diese Existent und 
	 * eine echte Datei ist und ueberschreibt dabei ihren Inhalt.
	 *
	 * <b>Alle Objekt sowie UnterObjekte muessen das Interface java.io.Serializable implementieren</b>
	 *
	 * @param 		object 	Das zu speichernde Objekt
	 * @param 		fileName	Der Dateiname in die das Objekt gespeichert werden soll.
	 * @exception	NullPointerException Wird geworfen falls der String oder das Objekt leer ist.
	 * @exception	StringIsEmptyException Wird geworfen falls ein leerer String uebergeben wird.
	 */
	public static void saveObject(Object object, String fileName) throws Exception {
		
		File file;
		ObjectOutputStream output = null;
		
		if(object == null) 
			throw new NullPointerException(OBJECT_NULL);
		
		//file = 
		try {
			output = new ObjectOutputStream(
						new BufferedOutputStream(
						new FileOutputStream(fileName)));
		
			output.writeObject(object);	
		} catch(InvalidClassException ic) {
			System.err.println(ic);
			throw new CanNotWriteObjectException(INVALID_CLASS, ic);
		} catch(NotSerializableException ns) {
			System.err.println(ns);
			throw new CanNotWriteObjectException(NOT_SERIALIZABLE, ns);
		} catch (IOException io) {
			System.err.println(io);
			throw new CanNotWriteObjectException(WRITE_IO, io);
		} finally {
			output.flush();
			output.close();		
		}
	}
	
	/**
	 * Speichert ein Objekt vom Typ Serializable in der Datei 
	 * mit dem Name der Variable fileName, wenn create true gesetzt wird
	 * wird die Datei angeleget falls sie nicht existiert,bei false wird keine
	 * neue Datei angelegt.
	 * Die Datei wird beim Speichern falls sie vorhanden ist ueberschrieben.
	 *
	 * <b>Alle Objekt sowie UnterObjekte muessen das Interface java.io.Serializable implementieren</b>
	 *
	 * @param 		object 	Das zu speichernde Objekt
	 * @param 		fileName	Der Dateiname in die das Objekt gespeichert werden soll.
	 * @ override	true wenn die Datei ueberschrieben werden soll, false wenn die Datei nicht ueberschrieben werden soll;
	 * @exception	NullPointerException Wird geworfen falls der String oder das Objekt leer ist.
	 * @exception	StringIsEmptyException Wird geworfen falls ein leerer String uebergeben wird.
	 * @exception 	FileAlreadyExcistsException Wird geworfen falls die angegebene Datei nicht ueberschrieben werden soll, diese jedoch existiert. Falls die Datei ueberschrieben werden soll wird dieser Fehler nicht geworfen.
	 */
	public static void saveObject(Object objekt, String fileName, boolean override) throws Exception {
		checkString(fileName);
		
		createFile(fileName, override);
		saveObject(objekt, fileName);	
	} 
	
	/**
	 * Laed ein Objekt aus einer Datei und gibt dieses zurueck.
	 * Dabei kann der angegebene Dateiname Absolut oder Relativ zum 
	 * Arbeitsverzeichniss angegeben werden.
	 * 
	 * @param fileName Der Dateiname der Datei die das Objekt enthaellt.
	 * @return Das Objekt das in der Datei gespeichert war.
	 * @exception NullPointerException Wird geworfen falls der Dateiname leer ist.
	 * @exception StringIsEmptyException Wird geworfen falls der uebergebene Dateiname leer ist.
	 * @exception CanNotLoadObjectException Wird geworfen falls es Probleme beim laden des Objektes
	 *										gibt, der Grund wird in Fehlerausgabe gegeben, sowie 
	 *										als Aufrufparameter fuer die Exception.
	 */
	public static Object loadObject(String fileName) throws Exception {
		File file;
		Object object;
		ObjectInputStream input = null;
		
		checkString(fileName);
		
		try {
			file = openFile(fileName);
			input = new ObjectInputStream(
						new BufferedInputStream(
						new FileInputStream(file)));
		
			object = input.readObject();
		} catch(ClassNotFoundException cnf) {
			System.err.println(cnf);
			throw new CanNotLoadObjectException(CLASS_NOT_FOUND, cnf);
		} catch(InvalidClassException ice) {
			System.err.println(ice);
			throw new CanNotLoadObjectException(INVALID_CLASS, ice);
		} catch(StreamCorruptedException sc) {
			System.err.println(sc);
			throw new CanNotLoadObjectException(STREAM_CORRUPTED, sc);
		} catch(OptionalDataException od) {
			System.err.println(od);
			throw new CanNotLoadObjectException(OPTIONAL_DATA, od);
		} catch(IOException io) {
			System.err.println(io);
			throw new CanNotLoadObjectException(LOAD_IO, io);
		} finally {
			input.close();
		}
		
		return object;
	}
}